package mybatis;

import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import mybatis.entity.UserEntity;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.junit.Test;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.function.Function;

/**
 * Lambda 也是可以像普通的对象一样进行序列化/反序列化的，就可以像普通对象一样先序列化成字节码然后进行存储或者传输，待需要的时候再将字节码进行反序列化，
 * 这样就实现了将一个方法进行了本地存储和传输，通过该方式可以进一步实现类似 Groovy 动态脚本一样的功能
 *
 * Lambda 的展示形式主要有两种：
 *    表达式      numbers.sort((a,b)->a.compareTo(b))
 *    方法引用    numbers.sort(Integer::compareTo)  (仅当该方法不修改lambda表达式提供的参数)
 * 如果不涉及到捕获 Lambda 所处的 Method/Class 等信息，2种方式没有太大区别
 *
 * 凡是继承了 Serializable 的函数式接口的实例都可以获取一个属于它的 SerializedLambda 实例，提供如下的功能：
 *    1.引用方法的 Method 信息（方法名，签名）
 *    2.Lambda 的 FunctionInterface 信息（方法名，签名）
 *    3.引用方法的所在的 Class
 *    4.Lambda 里面捕获的变量信息
 *
 * @author jackcfan
 * @date 22/9/15
 */
public class LambdaTest {

  @Test
  public void lambda() throws Exception{
    /**
     * 未实现序列化的 lambda 接口，没有 writeReplace 方法
     * */
    Function<UserEntity, Long> notSerializedLambda = UserEntity::getId;
    try{
      Method writeReplace = notSerializedLambda.getClass().getDeclaredMethod("writeReplace");
    }catch (NoSuchMethodException e){
      System.out.println("notSerializedLambda writeReplace not found");
    }

    /**
     * SFunction 实现了 Serializable 接口，编译器会自动创建 writeReplace 方法，并返回 SerializedLambda 实例
     *      <code>private SerializedLambda writeReplace(){}<code/>
     * SerializedLambda 实例中可获取到这个函数式接口的运行时信息
     * */
    SFunction<UserEntity, Long> func = UserEntity::getId;
    // func = (UserEntity u) -> {return u.getId();};         // 不要使用这种写法，否则无法正确获取源信息

    Method method = func.getClass().getDeclaredMethod("writeReplace");
    method.setAccessible(true);
    SerializedLambda serializedLambda = (SerializedLambda)method.invoke(func);

    System.out.println(serializedLambda);
    // 获取 lambda 元信息
    System.out.println("implClass="+serializedLambda.getImplClass());
    // 函数式接口方法的实现方法名称
    System.out.println("implMethod="+serializedLambda.getImplMethodName());
    // 自动推断字段名
    System.out.println("fieldName="+PropertyNamer.methodToProperty(serializedLambda.getImplMethodName()));
  }
}
